(function (window, document, $) {
	function Game2048(opt) {
		var winNum = 2048; // 胜利条件
		var isGameOver = true; // 游戏是否已经结束
		var prefix = opt.prefix, len = opt.len, size = opt.size, margin = opt.margin;
		var view = new View(prefix, len, size, margin);
		view.init();
		var board = new Board(len);
		board.init();
		board.onGenerate = function (e) {
			view.addNum(e.x, e.y, e.num); // 替换原来的“console.log(e);”
		};
		board.generate();
		board.generate();
		var score = 0; // 通过变量保存分数
		board.onMove = function (e) {
			if (e.to.num >= winNum) {
				isGameOver = true;
				setTimeout(function () { view.win(); }, 300);
			}
			if (e.to.num > e.from.num) {
				score += e.to.num; // 累加分数
				view.updateScore(score); // 更新页面中显示的分数
			}
			// 每当 border.arr 中的单元格移动时，调用此方法控制页面中的单元格移动
			view.move(e.from, e.to);
		};
		board.onMoveComplete = function (e) {
			// 判断是否失败
			if (!board.canMove()) {
				isGameOver = true;
				setTimeout(function () { view.over(score); }, 300);
			}
			if (e.moved) {
				// 一次移动操作全部结束后，如果移动成功，则在棋盘中增加一个新单元格
				setTimeout(function () { board.generate(); }, 200);

			}
		};
		$(document).keydown(function (e) {
			if (isGameOver) {
				return false;
			}
			switch (e.which) {
				case 37: board.moveLeft(); break; // 左移
				case 38: board.moveUp(); break; // 上移
				case 39: board.moveRight(); break; // 右移
				case 40: board.moveDown(); break; // 下移
			}
		});

		function start() { // 开始游戏
			score = 0; // 将保存的分数重置为 0
			view.updateScore(0); // 将页面中的分数重置为 0
			view.cleanNum(); // 清空页面中多余的数字单元格
			board.init(); // 初始化单元格数组
			board.generate(); // 生成第 1 个数字
			board.generate(); // 生成第 2 个数字
			isGameOver = false; // 将游戏状态设为开始
		}
		$('#' + prefix + '_restart').click(start); // 为“重新开始”按钮添加单击事件
		start(); // 开始游戏
	}
	window['Game2048'] = Game2048;

})(window, document, jQuery);

function View(prefix, len, size, margin) {
	this.prefix = prefix; // id 或 class 前缀
	this.len = len; // 棋盘单边单元格数量（总数量为 len * len）
	this.size = size; // 单元格边长，单元格大小为 size×size
	this.margin = margin; // 单元格间距
	this.container = $('#' + prefix + '_container');
	var containerSize = len * size + margin * (len + 1);
	this.container.css({ width: containerSize, height: containerSize });
	this.nums = {}; // 保存所有数字单元格对象
	this.score = $('#' + prefix + '_score');
}

View.prototype = {
	getPos: function (n) {
		return this.margin + n * (this.size + this.margin);

	},
	init: function () {
		for (var x = 0, len = this.len; x < len; ++x) {
			for (var y = 0; y < len; ++y) {
				var $cell = $('<div class="' + this.prefix + '-cell"></div>');
				$cell.css({
					width: this.size + 'px', height: this.size + 'px',
					top: this.getPos(x), left: this.getPos(y)
				}).appendTo(this.container);
			}
		}
	},

	addNum: function (x, y, num) {
		var $num = $('<div class="' + this.prefix + '-num ' +
			this.prefix + '-num-' + num + ' ">');
		$num.text(num).css({
			top: this.getPos(x) + parseInt(this.size / 2), // 用于从中心位置展开
			left: this.getPos(y) + parseInt(this.size / 2) // 用于从中心位置展开
		}).appendTo(this.container).animate({
			width: this.size + 'px', height: this.size + 'px',
			lineHeight: this.size + 'px',
			top: this.getPos(x), left: this.getPos(y)
		}, 100);
		this.nums[x + '-' + y] = $num;
	},

	move: function (from, to) {
		var fromIndex = from.x + '-' + from.y, toIndex = to.x + '-' + to.y;
		var clean = this.nums[toIndex];
		this.nums[toIndex] = this.nums[fromIndex];
		delete this.nums[fromIndex];
		var prefix = this.prefix + '-num-';
		var pos = { top: this.getPos(to.x), left: this.getPos(to.y) };
		this.nums[toIndex].finish().animate(pos, 200, function () {
			if (to.num > from.num) { // 判断数字是否合并（合并后 to.num 大于 from.num）
				clean.remove();
				$(this).text(to.num).removeClass(prefix + from.num).addClass(prefix + to.num);
			}
		});
	},

	updateScore: function (score) {
		this.score.text(score);
	},
	win: function () {
		$('#' + this.prefix + '_over_info').html('<p>您获胜了</p>');
		$('#' + this.prefix + '_over').removeClass(this.prefix + '-hide');
	},
	over: function (score) {
		$('#' + this.prefix + '_over_info').html('<p>本次得分</p><p>' + score + '</p>');
		$('#' + this.prefix + '_over').removeClass(this.prefix + '-hide');
	},

	cleanNum: function () {
		this.nums = {};
		$('#' + this.prefix + '_over').addClass(this.prefix + '-hide');
		$('.' + this.prefix + '-num').remove();
	},
};

function Board(len) {
	this.len = len;
	this.arr = [];

}
Board.prototype = {
	init: function () {
		for (var arr = [], len = this.len, x = 0; x < len; ++x) {
			arr[x] = [];
			for (var y = 0; y < len; ++y) {
				arr[x][y] = 0;
			}
		}
		this.arr = arr;
	},


	// 随机生成数字 2 或 4，保存到数组的随机位置
	generate: function () {
		var empty = [];
		// 查找数组中所有值为 0 的元素索引
		for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) {
			for (var y = 0; y < len; ++y) {
				if (arr[x][y] === 0) {
					empty.push({ x: x, y: y });

				}

			}

		}
		if (empty.length < 1) {
			return false;

		}
		var pos = empty[Math.floor((Math.random() * empty.length))];
		this.arr[pos.x][pos.y] = Math.random() < 0.5 ? 2 : 4;
		this.onGenerate({ x: pos.x, y: pos.y, num: this.arr[pos.x][pos.y] });

	},
	// 每当 generate()方法被调用时，执行此方法
	onGenerate: function () { },

	moveLeft: function () {
		console.log("left");
		var moved = false; // 是否有单元格被移动
		// 外层循环从上到下遍历“行”，内层循环从左到右遍历“列”
		for (var x = 0, len = this.arr.length; x < len; ++x) {
			for (var y = 0, arr = this.arr[x]; y < len; ++y) {
				// 从 y + 1 位置开始，向右查找
				for (var next = y + 1; next < len; ++next) {
					// 如果 next 单元格是 0，找下一个不是 0 的单元格
					if (arr[next] === 0) {
						continue;
					}
					// 如果 y 单元格数字是 0，则将 next 移动到 y 位置，然后将 y 减 1 重新查找
					if (arr[y] === 0) {
						arr[y] = arr[next];
						this.onMove({
							from: { x: x, y: next, num: arr[next] },
							to: { x: x, y: y, num: arr[y] }
						});
						arr[next] = 0;
						moved = true;
						--y;
						// 如果 y 与 next 单元格数字相等，则将 next 移动并合并给 y
					} else if (arr[y] === arr[next]) {
						arr[y] *= 2;
						this.onMove({
							from: { x: x, y: next, num: arr[next] },
							to: { x: x, y: y, num: arr[y] }
						});
						arr[next] = 0;
						moved = true;
					}
					break;
				}
			}
		}
		this.onMoveComplete({ moved: moved });
	},

	moveRight: function () {
		console.log("Right");
		var moved = false; // Indicates whether any cell has been moved

		// Outer loop traverses rows from top to bottom
		for (var x = 0, len = this.arr.length; x < len; ++x) {
			// Inner loop traverses columns from right to left
			for (var y = len - 1, arr = this.arr[x]; y >= 0; --y) {
				// Start checking from the previous column to the left
				for (var prev = y - 1; prev >= 0; --prev) {
					// Skip if the previous cell is zero
					if (arr[prev] === 0) {
						continue;
					}
					// If current cell is zero, move the previous cell here and recheck current position
					if (arr[y] === 0) {
						arr[y] = arr[prev];
						this.onMove({
							from: { x: x, y: prev, num: arr[prev] },
							to: { x: x, y: y, num: arr[y] }
						});
						arr[prev] = 0;
						moved = true;
						++y; // Recheck current position
						// If current cell and previous cell are equal, merge them
					} else if (arr[y] === arr[prev]) {
						arr[y] *= 2;
						this.onMove({
							from: { x: x, y: prev, num: arr[prev] },
							to: { x: x, y: y, num: arr[y] }
						});
						arr[prev] = 0;
						moved = true;
					}
					break; // Exit inner loop to continue with the next position
				}
			}
		}
		this.onMoveComplete({ moved: moved });
	},

	moveDown: function () {
		console.log("Down");
		var moved = false; // Indicates whether any cell has been moved

		// Outer loop traverses columns from left to right
		for (var y = 0, len = this.arr.length; y < len; ++y) {
			// Inner loop traverses rows from bottom to top
			for (var x = len - 1; x >= 0; --x) {
				// Start checking from the previous row above
				for (var prev = x - 1; prev >= 0; --prev) {
					// Skip if the previous cell is zero
					if (this.arr[prev][y] === 0) {
						continue;
					}
					// If current cell is zero, move the previous cell here and recheck current position
					if (this.arr[x][y] === 0) {
						this.arr[x][y] = this.arr[prev][y];
						this.onMove({
							from: { x: prev, y: y, num: this.arr[prev][y] },
							to: { x: x, y: y, num: this.arr[x][y] }
						});
						this.arr[prev][y] = 0;
						moved = true;
						++x; // Recheck current position
						// If current cell and previous cell are equal, merge them
					} else if (this.arr[x][y] === this.arr[prev][y]) {
						this.arr[x][y] *= 2;
						this.onMove({
							from: { x: prev, y: y, num: this.arr[prev][y] },
							to: { x: x, y: y, num: this.arr[x][y] }
						});
						this.arr[prev][y] = 0;
						moved = true;
					}
					break; // Exit inner loop to continue with the next position
				}
			}
		}
		this.onMoveComplete({ moved: moved });
	},

	moveUp: function () {
		console.log("Up");
		var moved = false; // Indicates whether any cell has been moved

		// Outer loop traverses columns from left to right
		for (var y = 0, len = this.arr.length; y < len; ++y) {
			// Inner loop traverses rows from top to bottom
			for (var x = 0; x < len; ++x) {
				// Start checking from the next row below
				for (var next = x + 1; next < len; ++next) {
					// Skip if the next cell is zero
					if (this.arr[next][y] === 0) {
						continue;
					}
					// If current cell is zero, move the next cell here and recheck current position
					if (this.arr[x][y] === 0) {
						this.arr[x][y] = this.arr[next][y];
						this.onMove({
							from: { x: next, y: y, num: this.arr[next][y] },
							to: { x: x, y: y, num: this.arr[x][y] }
						});
						this.arr[next][y] = 0;
						moved = true;
						--x; // Recheck current position
						// If current cell and next cell are equal, merge them
					} else if (this.arr[x][y] === this.arr[next][y]) {
						this.arr[x][y] *= 2;
						this.onMove({
							from: { x: next, y: y, num: this.arr[next][y] },
							to: { x: x, y: y, num: this.arr[x][y] }
						});
						this.arr[next][y] = 0;
						moved = true;
					}
					break; // Exit inner loop to continue with the next position
				}
			}
		}
		this.onMoveComplete({ moved: moved });
	},



	onMove: function () { },
	onMoveComplete: function () { },

	canMove: function () {
		for (var x = 0, arr = this.arr, len = arr.length; x < len; ++x) {
			for (var y = 0; y < len; ++y) {
				if (arr[x][y] === 0) {
					return true;
				}
				var curr = arr[x][y], right = arr[x][y + 1];
				var down = arr[x + 1] ? arr[x + 1][y] : null;
				if (right === curr || down === curr) {
					return true;
				}
			}
		}
		return false;
	},
};




